use opentelemetry::{KeyValue, trace::TracerProvider as _};
use opentelemetry_sdk::{
    Resource,
    trace::{RandomIdGenerator, Sampler, SdkTracerProvider},
};
use opentelemetry_semantic_conventions::{SCHEMA_URL, attribute::SERVICE_VERSION};
use tracing_appender::non_blocking::WorkerGuard;
use tracing_core::{Level, LevelFilter};
use tracing_opentelemetry::OpenTelemetryLayer;
use tracing_subscriber::{EnvFilter, Layer};
use tracing_subscriber::{filter::Targets, layer::SubscriberExt, util::SubscriberInitExt};

// Create a Resource that captures information about the entity for which telemetry is recorded.
fn resource() -> Resource {
    Resource::builder()
        .with_service_name(std::env::var("OTEL_SERVICE_NAME").unwrap_or_default())
        .with_schema_url(
            [KeyValue::new(
                SERVICE_VERSION,
                std::env::var("OTEL_SERVICE_VERSION").unwrap_or_default(),
            )],
            SCHEMA_URL,
        )
        .build()
}

// Construct TracerProvider for OpenTelemetryLayer
fn init_tracer_provider() -> SdkTracerProvider {
    let exporter = opentelemetry_otlp::SpanExporter::builder()
        .with_http()
        .build()
        .unwrap();

    SdkTracerProvider::builder()
        // Customize sampling strategy
        .with_sampler(Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased(
            1.0,
        ))))
        // If export trace to AWS X-Ray, you can use XrayIdGenerator
        .with_id_generator(RandomIdGenerator::default())
        .with_resource(resource())
        .with_batch_exporter(exporter)
        .build()
}

// Initialize tracing-subscriber and return OtelGuard for opentelemetry-related termination processing
pub(crate) fn init_tracing_subscriber(log_level: Level) -> LoggerGuard {
    let tracer_provider = init_tracer_provider();
    
    let file_appender = tracing_appender::rolling::daily("logs", "app.log");
    let (non_blocking_file, _file_guard) = tracing_appender::non_blocking(file_appender);

    let tracer = tracer_provider.tracer("tracing-otel-subscriber");
    // 创建分层过滤器：允许应用代码使用更详细的日志级别，但限制 OpenTelemetry 相关库的日志级别
    let stdout_filter = EnvFilter::builder()
        .with_default_directive(log_level.into()) // 应用默认日志级别
        .from_env_lossy();

    let file_filter = EnvFilter::builder()
        .with_default_directive(log_level.into()) // 应用默认日志级别
        .from_env_lossy();

    // 为 OpenTelemetry layer 创建专门的过滤器
    let otel_layer_filter = Targets::new()
        .with_target("opentelemetry_sdk", Level::INFO)
        .with_target("quinn", Level::WARN)
        .with_default(Level::TRACE);       // 其他所有库默认 TRACE

    tracing_subscriber::registry()
        // 全局基础过滤器 - 防止网络栈重入
        .with(LevelFilter::DEBUG)
        // 文件输出层
        .with(
            tracing_subscriber::fmt::layer()
                .with_writer(non_blocking_file)
                .with_ansi(false)
                .with_filter(file_filter)
        )
        // 控制台输出层 - 仅INFO
        .with(
            tracing_subscriber::fmt::layer()
                .with_filter(stdout_filter)
        )
        // OpenTelemetry 层 - 使用目标过滤器
        .with(
            OpenTelemetryLayer::new(tracer)
                .with_filter(otel_layer_filter)
        )
        .init();

    LoggerGuard {
        tracer_provider,
        _file_guard,
    }
}

pub(crate) struct LoggerGuard {
    tracer_provider: SdkTracerProvider,
    _file_guard: WorkerGuard,
}

impl Drop for LoggerGuard {
    fn drop(&mut self) {
        if let Err(err) = self.tracer_provider.shutdown() {
            eprintln!("{err:?}");
        }
    }
}